﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using JSONUtil;

namespace TranslationAPI
{
    internal class GoogleTranslator : ITranslator
    {
        static UriBuilder uriBuilder = new UriBuilder("http://ajax.googleapis.com/ajax/services/language/translate?v=1.0");
        static int baselength = uriBuilder.Uri.ToString().Length;
        //string url = http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=hello%20world&langpair=en%7Cit&callback=foo&context=bar;
        //static string query = "format=HTML&v=1.0&q={0}&langpair={1}";

        IWebProxy proxy = WebRequest.GetSystemWebProxy();
        static int lengthLimit = 5000;

        string referer = "http://www.codeplex.com/translationapi";

        public GoogleTranslator(string referer)
        {
            this.referer = referer;
        }


        public string Translate(string text, string inputlanguage, string outputlanguage)
        {
            IEnumerable<string> texts = Translate(new string[] { text }, inputlanguage, outputlanguage);

            foreach (string translatedText in texts)
            {
                return translatedText;
            }

            return null;
        }

        static string GetQuery(string[] texts, string langpair)
        {
            StringBuilder sb = new StringBuilder();

            foreach (string text in texts)
            {
                //sb.AppendFormat("q={0}&", Uri.EscapeDataString(String.Format("<div>{0}</div>",text)));
                sb.AppendFormat("q={0}&", Uri.EscapeDataString(text));
            }
            sb.AppendFormat("langpair={0}", langpair);

            return sb.ToString();
        }

        public IEnumerable<string> Translate(IEnumerable<string> texts, string inputlanguage, string outputlanguage)
        {
            if (texts == null)
                yield break;

            List<string> outputs = new List<string>();

            foreach (string uri in GetUrlStrings(texts, inputlanguage, outputlanguage))
            {
                if (uri == null)
                {
                    yield return "error: text limit exceeded";
                    continue;
                }

                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
                request.Proxy = proxy;// new WebProxy("proxy", 8080);

                if (!String.IsNullOrEmpty(referer))
                    request.Referer = referer;

                WebResponse response = request.GetResponse();

                using (StreamReader str = new StreamReader(response.GetResponseStream()))
                {
                    string data = str.ReadToEnd();
                    string[] tmpoutputs = GetTexts(data);

                    foreach (string tmpoutput in tmpoutputs)
                        yield return tmpoutput;
                }
            }

            /*
            string[] outputs = new string[texts.Length];
            for (int i = 0; i < texts.Length; i++)
            {
                outputs[i] = Translate(texts[i], inputlanguage, outputlanguage);
            }
            */
            yield break;
        }

        static IEnumerable<string> GetUrlStrings(IEnumerable<string> texts, string inputlanguage, string outputlanguage)
        {
            string languagepair = inputlanguage + "|" + outputlanguage;
            
            string baseurl = String.Format("{0}&langpair={1}", uriBuilder.Uri, languagepair);

            int querylimit = lengthLimit - baseurl.Length;
            
            StringBuilder sb = new StringBuilder();

            IEnumerator<string> textsEnumerator = texts.GetEnumerator();
            while(true)
            {
                if (textsEnumerator.MoveNext())
                {
                    string q = "&q=" + Uri.EscapeDataString(textsEnumerator.Current);
                    int querylen = sb.Length + q.Length;
                    if (querylen > querylimit)
                    {
                        if (sb.Length == 0)
                            yield return null;
                        else
                        {
                            yield return baseurl + sb.ToString();
                            sb = new StringBuilder();
                            if (q.Length > querylimit)
                                yield return null;
                            else
                                sb.Append(q);
                        }
                    }
                    else
                        sb.Append(q);
                }
                else
                {
                    //return the url for queries built till now.
                    if (sb.Length > 0)
                        yield return baseurl + sb.ToString();
                    
                    //get out of the loop
                    break;
                }
            }
        }

        static string[] GetTexts(string jsonData)
        {
            JSONObject response = (JSONObject)JSONSerializer.Deserialize(jsonData);
            return GetTexts(response);
        }

        static string[] GetTexts(JSONObject responseObject)
        {
            /*
            {
             "responseData": {"translatedText":"Hallo Welt"}, 
             "responseDetails": null, 
             "responseStatus": 200
            }
             */
            /*
            {
             "responseData": null, 
             "responseDetails": \"error with translation.\", 
             "responseStatus": 400
            }
             */

            List<string> texts = new List<string>();
            int responseStatus = (int)responseObject["responseStatus"];
            if (responseStatus == 200)
            {
                IJSONObject responseData = (IJSONObject)responseObject["responseData"];
                if (responseData is JSONArray)
                {
                    JSONArray jsArray = (JSONArray)responseData;
                    for (int i = 0; i < jsArray.Items.Count; i++)
                    {
                        texts.AddRange(GetTexts((JSONObject)jsArray.Items[i]));
                    }
                }
                else
                {
                    JSONObject jsObject = (JSONObject)responseData;
                    string translatedText = (string)jsObject["translatedText"];
                    texts.Add(translatedText);
                }
            }
            else
            {
                string responseDetails = (string)responseObject["responseDetails"];
                texts.Add(responseDetails);
            }

            return texts.ToArray();
        }
    }
}
